using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
//using ApplicationCore.EnzymeProcessingDataSetTableAdapters;
using System.Diagnostics;
using System.ComponentModel;
using System.Linq;

namespace ApplicationCore
{
    public class ProgressEventArgs : EventArgs
    {
        private int progress;
        private string subStep;
        private string mainStep;
      

        public string MainStep
        {
            get { return mainStep; }
        }


        public string SubStep
        {
            get { return subStep; }
        }


        public int Progress
        {
            get { return progress; }
        }
        public ProgressEventArgs(int progress, string mainStep, string subStep)
        {
            this.subStep = subStep;
            this.progress = progress;
            this.mainStep = mainStep;
        }
    }

    public class PostProcessor
    {

        private SortedDictionary<string, SortedDictionary<string, double?>> sampleMatrix;
        private SortedDictionary<string, SortedDictionary<string, double?>> sampleMatrixWorkCopy;

        private SortedDictionary<string, object[]> sampleDescription;
        private SortedDictionary<string, double> centeringFactors;
        private List<string> enzymes;
        private SqlConnection con;
        private string _experiment;
      //  private EnzymeProcessingDataSet.SelectProcessedData2DataTable fetchedTable;


        //public event EventHandler<ProgressEventArgs> Progress;

        //protected void OnProgress(int progress, string mainStep, string subStep)
        //{
        //    if (this.Progress != null)
        //    {
        //        if (progress > 100)
        //               Progress(this, new ProgressEventArgs(100,mainStep,subStep));
        //        else
        //               Progress(this, new ProgressEventArgs(progress, mainStep, subStep));
        //    }
        //}

        


        public PostProcessor()
        {
            this.sampleMatrix = new SortedDictionary<string, SortedDictionary<string, double?>>();
            this.sampleDescription = new SortedDictionary<string, object[]>();
            this.centeringFactors = new SortedDictionary<string, double>();
            this.lastOption = PostProcessorOption.None;
        }


        private void CreateWorkCopy()
        {
            this.sampleMatrixWorkCopy = new SortedDictionary<string, SortedDictionary<string, double?>>();
            foreach (string sample in sampleMatrix.Keys)
            {
                SortedDictionary<string, double?> sampleContent = new SortedDictionary<string, double?>();
                foreach (string enz in sampleMatrix[sample].Keys)
                {
                    sampleContent.Add(enz, sampleMatrix[sample][enz]);
                }
                this.sampleMatrixWorkCopy.Add(sample, sampleContent);
            }
        }


        private PostProcessorOption lastOption;
        public DataTable ApplyExportOption(PostProcessorOption option, int decimals, BackgroundWorker bw )
        {
            if (this.lastOption != option)
            {
                bw.ReportProgress(0, "Create working copy...");
                CreateWorkCopy();
                lastOption = option;
                if (option == PostProcessorOption.InterBatchCentering || option == PostProcessorOption.All)
                {
                    this.centeringFactors.Clear();
                    ApplyInterBatchCentering(bw);
                }

                if (option == PostProcessorOption.AliquotMerge || option == PostProcessorOption.All)
                {
                    ApplyAliquotMerge(bw);
                }
            }

            DataTable ret = BuildTable(this.enzymes, decimals,bw, option);
            return ret;
        }

        private void ApplyAliquotMerge(BackgroundWorker bw)
        {
            SortedDictionary<string, SortedDictionary<string, double?>> temp = new SortedDictionary<string, SortedDictionary<string, double?>>();
            List<int> samples = new List<int>();

            bw.ReportProgress(0, "Init merging...");
            foreach (SelectProcessedData2Result pdr in _data)
            {
                if (!samples.Contains(pdr.Sample_Fk))
                    samples.Add(pdr.Sample_Fk);
            }

            samples.Sort();

            bw.ReportProgress(10, "Enter merging...");
            double progressStep = 90 / (double)(samples.Count);
            double currentStep = 10;

            foreach (int s in samples)
            {

                currentStep += progressStep;
                bw.ReportProgress((int)currentStep, "Merging sample " + s);
                string newsamId = this._experiment + "-" + s + "-...";
                temp.Add(newsamId, new SortedDictionary<string, double?>());
       
                foreach (string enz in enzymes)
                {
                    double mergedVal = 0;
                    int c = 0;
                    foreach (string samp in AliquotsOfSample(s))
                    {
                        if (sampleMatrixWorkCopy[samp][enz] != null)
                        {
                            mergedVal += (double)sampleMatrixWorkCopy[samp][enz];
                            c++;
                        }
                    }
                    if (c > 0)
                    {
                        mergedVal = mergedVal / (double)c;
                        temp[newsamId].Add(enz, mergedVal);
                    }
                    else
                        temp[newsamId].Add(enz, null);
                    
                }
            }

            this.sampleMatrixWorkCopy = temp;
        }

        private List<string> AliquotsOfSample(int sample)
        {
            List<string> ret = new List<string>();
            foreach (string samp in this.sampleMatrixWorkCopy.Keys)
            {
                string[] split = samp.Split("-".ToCharArray());
                int s = Convert.ToInt32(split[1]);
                if (sample == s)
                    ret.Add(samp);
            }
            return ret;
        }


        private EnzymeDBDataContext _dataContext;
        public void LoadData(string experiment, EnzymeDBDataContext con, BackgroundWorker bw )
        {

            this.enzymes = new List<string>();
            _dataContext = con;
            this._experiment = experiment;
            
            LoadProcessedValues(bw);
        }

        private void ApplyInterBatchCentering(BackgroundWorker bw)
        {
            FillCenteringFactors();
            double progressStep = 100 / (double)(sampleMatrixWorkCopy.Count * enzymes.Count);
            double currentStep = 0;
            foreach(string enz in enzymes)
            {
                foreach (string sampleId in sampleMatrixWorkCopy.Keys)
                {
                    string key = StringHelper.BatchEnzymeString(sampleDescription[sampleId][0] as string, enz);
                    if (centeringFactors.ContainsKey(key))
                    {

                        double centeringFac = centeringFactors[key];

                        double? x = sampleMatrixWorkCopy[sampleId][enz];
                        if (x != null)
                            sampleMatrixWorkCopy[sampleId][enz] = x * centeringFac;
                    }
                    else
                    {
                    }
                    currentStep += progressStep;
                  }
                bw.ReportProgress(Convert.ToInt32(currentStep), "Applying centering factors to " + enz);

            }
        }

        private void FillCenteringFactors()
        {
            foreach(string enz in enzymes)
            {
                double allAvg = 0;
                List<InterBatchAvgResult> tab = _dataContext.InterBatchAvg(_experiment, enz).ToList<InterBatchAvgResult>();
                //ta.Connection = con;
                //EnzymeProcessingDataSet.InterBatchAvgDataTable tab = new EnzymeProcessingDataSet.InterBatchAvgDataTable();
                //ta.Fill(tab, _experiment, enz);
                foreach (InterBatchAvgResult row in tab)
                {
                    if(row.Average.HasValue)
                        allAvg += row.Average.Value ;
                }
                allAvg = allAvg / tab.Count;
                foreach (InterBatchAvgResult row in tab)
                {
                    centeringFactors.Add(StringHelper.BatchEnzymeString(row.BatchName_Fk, row.BatchNumber_Fk, row.BatchDate_Fk, enz), allAvg / row.Average.Value);
                }
            }
        }

        private DataTable BuildTable(List<string> enzymes, int decimals, BackgroundWorker bw, PostProcessorOption option)
        {
            SortedDictionary<string, SortedDictionary<string, double?>> usedMatrix;

            if (lastOption == PostProcessorOption.None)
                usedMatrix = sampleMatrix;
            else
                usedMatrix = sampleMatrixWorkCopy;

            DataTable ret = new DataTable("ProcVals");

            if (option != PostProcessorOption.AliquotMerge && option != PostProcessorOption.All)
                ret.Columns.Add("Batch", typeof(string));
            ret.Columns.Add("Experiment", typeof(string));
            ret.Columns.Add("Sample", typeof(int));
            if (option != PostProcessorOption.AliquotMerge && option != PostProcessorOption.All)
            {
                ret.Columns.Add("Aliquot", typeof(string));
            }
           for (int e = 0; e < enzymes.Count; e++)
           {
                DataColumn dt = new DataColumn(enzymes[e],typeof(double));
                dt.AllowDBNull = true;
                ret.Columns.Add(dt);
            }

            if (option != PostProcessorOption.AliquotMerge && option != PostProcessorOption.All)
                ret.PrimaryKey = new DataColumn[3] { ret.Columns[1], ret.Columns[2], ret.Columns[3] };
            else
                ret.PrimaryKey = new DataColumn[2] { ret.Columns[0], ret.Columns[1]};
            double progressStep = 100 / (double)(usedMatrix.Count * enzymes.Count);
            double currentStep = 0;
            foreach (string sampId in usedMatrix.Keys)
            {
                string[] temp = sampId.Split("-".ToCharArray());
                string exp = temp[0];
                int sample = Convert.ToInt32(temp[1]);
                int offSet;
                object[] items;

                if (option != PostProcessorOption.AliquotMerge && option != PostProcessorOption.All)
                {
                    offSet = 4;
                    items = new object[enzymes.Count + offSet];
                    int ali = Convert.ToInt32(temp[2]);
                    items[0] = sampleDescription[sampId][0];
                    items[1] = exp;
                    items[2] = sample;
                    items[3] = ali;
                }
                else
                {
                    offSet = 2;
                    items = new object[enzymes.Count + offSet];
                    items[0] = exp;
                    items[1] = sample;
                }

            

                for (int e = 0; e < enzymes.Count; e++)
                {
                    double? val = usedMatrix[sampId][enzymes[e]];

                    if (val != null)
                        val = Math.Round((double)val, decimals);
                    items[e + offSet] = val;

                    currentStep += progressStep;
                }
                bw.ReportProgress(Convert.ToInt32(currentStep), "Building result table: " + StringHelper.SampleString(exp, sample, -1));
                ret.Rows.Add(items);
            }
            return ret;
        }

        IEnumerable<SelectProcessedData2Result> _data;
        private void LoadProcessedValues(BackgroundWorker bw )
        {
           
           

            //SelectProcessedData2TableAdapter spdAdapter = new SelectProcessedData2TableAdapter();
            //spdAdapter.Connection = con;
            //fetchedTable = new EnzymeProcessingDataSet.SelectProcessedData2DataTable();
            bw.ReportProgress(5, "Query data set");
            //spdAdapter.Fill(fetchedTable, experiment);
            _data = (_dataContext.SelectProcessedData2(this._experiment)).ToList<SelectProcessedData2Result>();
  

            bw.ReportProgress(10, "Evaluate retrieved data");
            enzymes = new List<string>();
            List<int> samples = new List<int>();
            foreach (var pdr in _data)
            {
                if (!enzymes.Contains(pdr.Analyte))
                    enzymes.Add(pdr.Analyte);

                if (!samples.Contains(pdr.Sample_Fk))
                    samples.Add(pdr.Sample_Fk);
              
            }
            bw.ReportProgress(20, "Compile samples");
            double progressStep = (80 / (double)_data.Count());
            double currentStep = 20;
            samples.Sort();
            for (int s = 0; s < samples.Count; s++)
            {
                List<int> alis = ListAliquots(_data, samples[s]);
                for (int a = 0; a < alis.Count; a++)
                {
                    SortedDictionary<string, double?> vals = new SortedDictionary<string, double?>();
                    object[] descs = new object[1];
                    for (int e = 0; e < enzymes.Count; e++)
                    {

                        SelectProcessedData2Result spdr = _data.SingleOrDefault(x => 
                                            x.Analyte == enzymes[e] 
                                            && x.Aliquot_Fk == alis[a] 
                                            && x.Sample_Fk == samples[s] 
                                            && x.Experiment_Fk.Trim().ToLower() == _experiment.Trim().ToLower());
                        if (spdr != null)
                        {
                            vals.Add(enzymes[e], spdr.Value);
                            descs[0] = StringHelper.BatchString(spdr.BatchName_Fk, spdr.BatchNumber_Fk, spdr.BatchDate_Fk);
                            currentStep += progressStep;
                            
                        }
                        else
                            vals.Add(enzymes[e], null);

                    }
                    bw.ReportProgress(Convert.ToInt32(currentStep), "Compile sample : " + StringHelper.SampleString(_experiment, samples[s], alis[a]));
                    sampleMatrix.Add(StringHelper.SampleString(_experiment,samples[s],alis[a]), vals);
                    sampleDescription.Add(StringHelper.SampleString(_experiment, samples[s], alis[a]), descs);
                  }
              
            }
        }

        //gets all aliquots to given sample out of a data set
        private List<int> ListAliquots(IEnumerable<SelectProcessedData2Result> table, int sample)
        {
            List<int> aliquots = new List<int>();
            var samps = from x in table 
                        where x.Experiment_Fk.Trim().ToLower() == _experiment.Trim().ToLower()
                        where x.Sample_Fk == sample
                        select x;

            //EnzymeProcessingDataSet.SelectProcessedData2Row[] samps = (EnzymeProcessingDataSet.SelectProcessedData2Row[])table.Select("experiment_fk = '" + _experiment + "' and sample_fk = " + sample);
            foreach (var x in samps)
            {
                if (!aliquots.Contains(x.Aliquot_Fk))
                    aliquots.Add(x.Aliquot_Fk);
            }
            return aliquots;
        }

        

    }




    public enum PostProcessorOption
    {
        None,
        AliquotMerge,
        InterBatchCentering,
        All
    }

    class StringHelper
    {
        public static string BatchString(string BatchName, int number, DateTime date)
        {
            return BatchName + "-" + number + "-" + date.ToShortDateString();
        }

        public static string BatchEnzymeString(string BatchName, int number, DateTime date,string enzyme)
        {
            return BatchEnzymeString(BatchString( BatchName, number, date), enzyme);
        }

        public static string BatchEnzymeString(string batchString, string enzyme)
        {
            return batchString + "$" + enzyme;
        }

        public static string SampleString(string experiment, int sampel, int aliquot)
        {
            if(aliquot >=  0)
                return experiment + "-" + sampel + "-" + aliquot;
            else
                return experiment + "-" + sampel;
        }

    }
}
